package be.idamf.cortizone.fileprocessor
{
	import be.idamf.flash.events.dispatchEventOn;
	import be.idamf.flash.exec.pseudoThreadExec;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.FileListEvent;
	import flash.filesystem.File;
	
	[Event(name="cancel", type="flash.events.Event")]
	[Event(name="complete", type="flash.events.Event")]

	[DefaultProperty("processor")]
	
	/**
	 * File processor.
	 * 
	 * @author Jan Van Coppenolle
	 */
	public class FileProcessorEngine extends EventDispatcher
	{
		////////////////////////////////////////////////////////////////////////
		
		[Bindable]
		/**
		 * Number of files processed in one pass.
		 */
		public var processChunk:uint = 100;

		[Bindable]
		/**
		 * Max duration of one pass.
		 */
		public var timeFrame:uint = 0;
		
		
		[Bindable]
		/**
		 * Directory to process.
		 */
		public var directory:File;
		
		[Bindable]
		/**
		 * Wether or not to recurse into the sub-dictories.
		 */
		public var includeSubDirectories:Boolean;
		
		[Bindable]
		/**
		 * The processor that will do something with each file.
		 */
		public var processor:IFileProcessor;
		
		////////////////////////////////////////////////////////////////////////
		
		/**
		 * @private
		 */
		protected var dirs:Array;

		/**
		 * @private
		 */
		protected var files:Array;

		////////////////////////////////////////////////////////////////////////
		
		/**
		 * Contructor.
		 */
		public function FileProcessorEngine()
		{
		}
		
		////////////////////////////////////////////////////////////////////////
		
		private var _isBusy:Boolean;
		
		[Bindable]
		/**
		 * Indicates wether or not in the process of processing...
		 */
		public function get isBusy():Boolean
		{
			return _isBusy;
		}
		
		private function set isBusy(value:Boolean):void
		{
			_isBusy = value;
		}
		
		////////////////////////////////////////////////////////////////////////
		
		/**
		 * Starts processing.
		 */
		public function execute():void
		{
			if (!processor)
				throw new Error("No processor defined");
			
			if (!directory || !directory.isDirectory)
				throw new Error("No search directory defined");
			
			isBusy = true;
			dirs = [];
			files = [];

			processor.reset();
			
			processDirectory(directory);
		}
		
		/**
		 * Cancels processing.
		 */
		public function cancel():void
		{
			dirs = [];
			files = [];
			
			processor.cancel();

			dispatch(Event.CANCEL);
		}
		
		////////////////////////////////////////////////////////////////////////

		/**
		 * @private
		 */
		protected function processDirectory(dir:File):void
		{
			dir.addEventListener(FileListEvent.DIRECTORY_LISTING, directoryListingHandler);
			dir.getDirectoryListingAsync();
		}
		
		/**
		 * @private
		 */
		protected function directoryListingHandler(event:FileListEvent):void
		{
			File(event.target).removeEventListener(FileListEvent.DIRECTORY_LISTING, directoryListingHandler);
			files = event.files;
			pseudoThreadExec(threadedProcessor, timeFrame);
		}
		
		/**
		 * @private
		 */
		protected function threadedProcessor():Boolean
		{
			var i:int = processChunk;
			var file:File;
			
			while (--i != -1 && files.length != 0)
			{
				file = files.shift();
				
				if (file.isDirectory)
				{
					if (includeSubDirectories)
						dirs.push(file);
					
					continue;
				}
				
				processor.process(file);
			}
			
			if (files.length != 0)
				return true;
			
			if (dirs.length == 0)
			{
				isBusy = false;				
				dispatch(Event.COMPLETE);
			}
			else
			{
				processDirectory(dirs.shift());
			}
			
			return false;
		}
		
		/**
		 * @private
		 */
		protected function dispatch(type:String):void
		{
			dispatchEventOn(this, type);
		}
	}
}